<!DOCTYPE html>
<html lang="en"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>像素鸟</title>
<head>
<style>
.game {
  width: 800px;
  height: 600px;
  border: 2px solid;
  margin: 0 auto;
  position: relative;
  overflow: hidden;
}
.game .sky {
  background: url("./img1/sky.png");
  height: 100%;
  width: 200%;
  position: absolute;
  top: 0;
  left: 0;
}
.game .land {
  background: url("./img1/land.png");
  height: 112px;
  width: 200%;
  position: absolute;
  left: 0;
  bottom: 0;
}
.game .bird {
  background: url("./img1/bird.png");
  position: absolute;
  width: 33px;
  height: 26px;
  left: 150px;
  top: 150px;
}
.game .bird.swing1 {
  background-position: -8px -10px;
}
.game .bird.swing2 { 
  background-position: -60px -10px;
}
.game .bird.swing3 {
  background-position: -113px -10px;
}
.pipe {
  position: absolute;
  height: 100px;
  width: 52px;
  left: 500px;
}
.pipe.up {
  background: url("./img1/pipeDown.png");
  top: 0;
  background-position: bottom;
}
.pipe.down {
  background: url("./img1/pipeUp.png");
  bottom: 112px;
}
.info {
  text-align: center;
}
</style>
</head>
<body>
    <div class="game">
        <div class="sky"></div>
        <div class="land"></div>
        <div class="bird swing1"></div>
    </div>
    <p class="info">
        回车键开始/暂停，空格键跳跃
    </p>
    <script>
class Rectangle {
    constructor(width, height, left, top, xSpeed, ySpeed, dom) {
        this.width = width;
        this.height = height;
        this.left = left;
        this.top = top;
        this.xSpeed = xSpeed;
        this.ySpeed = ySpeed;
        this.dom = dom;
        this.render();
    }
    render() {
        this.dom.style.width = this.width + "px";
        this.dom.style.height = this.height + "px";
        this.dom.style.left = this.left + "px";
        this.dom.style.top = this.top + "px";
    }
    /**
     * 按照矩形的速度，和指定的时间，移动矩形
     * @param {*} duration 单位：秒
     */
    move(duration) {
        const xDis = this.xSpeed * duration; 
        const yDis = this.ySpeed * duration; 
        this.left = this.left + xDis;
        this.top = this.top + yDis; 
        if (this.onMove) {
            this.onMove(); 
        }
        this.render(); 
    }
}
const skyDom = document.querySelector(".sky");
const skyStyles = getComputedStyle(skyDom);
const skyWidth = parseFloat(skyStyles.width);
const skyHeight  = parseFloat(skyStyles.height);
class Sky extends Rectangle {
    constructor() {
        super(skyWidth, skyHeight, 0, 0, -50, 0, skyDom);
    }
    onMove() {
        if (this.left <= -skyWidth / 2) {
            this.left = 0;
        }
    }
}
const landDom = document.querySelector(".land");
const landStyles = getComputedStyle(landDom);
const landWidth = parseFloat(landStyles.width);
const landHeight = parseFloat(landStyles.height);
const landTop = parseFloat(landStyles.top);
class Land extends Rectangle {
    constructor(speed) {
        super(landWidth, landHeight, 0, landTop, speed, 0, landDom);
    }
    onMove() {
        if (this.left <= -landWidth / 2) {
            this.left = 0;
        }
    }
}
const birdDom = document.querySelector(".bird");
const birdStyles = getComputedStyle(birdDom);
const birdWidth = parseFloat(birdStyles.width);
const birdHeight = parseFloat(birdStyles.height);
const birdTop = parseFloat(birdStyles.top);
const birdLeft = parseFloat(birdStyles.left);
const gameDom = document.querySelector(".game");
const gameHeight = gameDom.clientHeight;
class Bird extends Rectangle {
  constructor() {
    super(birdWidth, birdHeight, birdLeft, birdTop, 0, 0, birdDom);
    this.g = 1500;
    this.maxY = gameHeight - landHeight - this.height;
    this.swingStatus = 1; 
    this.timer = null; 
    this.render();
  }
  startSwing() {
    if (this.timer) {
      return;
    }
    this.timer = setInterval(() => {
      this.swingStatus++;
      if (this.swingStatus === 4) {
        this.swingStatus = 1;
      }
      this.render();
    }, 200);
  }
  render() {
    super.render(); 
    this.dom.className = `bird swing${this.swingStatus}`;
  }
  stopSwing() {
    clearInterval(this.timer);
    this.timer = null;
  }
  move(duration) {
    super.move(duration); 
    this.ySpeed += this.g * duration;
  }
  onMove() {
    if (this.top < 0) {
      this.top = 0;
    } else if (this.top > this.maxY) {
      this.top = this.maxY;
    }
  }
  jump() {
    this.ySpeed = -300;
  }
}
const gameWidth = gameDom.clientWidth;
class Pipe extends Rectangle {
  constructor(height, top, speed, dom) {
    super(52, height, gameWidth, top, speed, 0, dom);
  }
  onMove() {
    if (this.left < -this.width) {
      this.dom.remove();
    }
  }
}
function getRandom(min, max) {
  return Math.floor(Math.random() * (max - min) + min);
}
class PipePare {
  constructor(speed) {
    this.spaceHeight = 160; 
    this.minHeight = 80; 
    this.maxHeight = landTop - this.minHeight - this.spaceHeight;
    const upHeight = getRandom(this.minHeight, this.maxHeight);
    const upDom = document.createElement("div");
    upDom.className = "pipe up";
    this.upPipe = new Pipe(upHeight, 0, speed, upDom); 
    const downHeight = landTop - upHeight - this.spaceHeight;
    const downTop = landTop - downHeight;
    const downDom = document.createElement("div");
    downDom.className = "pipe down";
    this.downPipe = new Pipe(downHeight, downTop, speed, downDom);
    gameDom.appendChild(upDom);
    gameDom.appendChild(downDom);
  }
  get useLess() {
    return this.upPipe.left < -this.upPipe.width;
  }
  move(duration) {
    this.upPipe.move(duration);
    this.downPipe.move(duration);
  }
}
class PipePareProducer {
  constructor(speed) {
    this.speed = speed;
    this.pairs = [];
    this.timer = null;
    this.tick = 1500;
  }
  startProduce() {
    if (this.timer) {
      return;
    }
    this.timer = setInterval(() => {
      this.pairs.push(new PipePare(this.speed));
      for (let i = 0; i < this.pairs.length; i++) {
        var pair = this.pairs[i];
        if (pair.useLess) {
          this.pairs.splice(i, 1);
          i--;
        }
      }
    }, this.tick);
  }
  stopProduce() {
    clearInterval(this.timer);
    this.timer = null;
  }
}
class Game {
    constructor() {
        this.sky = new Sky();
        this.land = new Land(-100);
        this.bird = new Bird();
        this.pipeProducer = new PipePareProducer(-100);
        this.timer = null;
        this.tick = 16;
        this.gameOver = false;
    }
    start() {
        if (this.timer) {
            return;
        }
        if (this.gameOver) {
            window.location.reload();
        }
        this.pipeProducer.startProduce(); 
        this.bird.startSwing();
        this.timer = setInterval(() => {
            const duration = this.tick / 1000;
            this.sky.move(duration)
            this.land.move(duration)
            this.bird.move(duration);
            this.pipeProducer.pairs.forEach(pair => {
                pair.move(duration);
            })
            if (this.isGameOver()) {
                this.stop();
                this.gameOver = true;
            }
        }, this.tick);
    }
    /**
     * 判断两个矩形是否碰撞
     * @param {*} rec1 
     * @param {*} rec2 
     */
    isHit(rec1, rec2) {
        var centerX1 = rec1.left + rec1.width / 2;
        var centerY1 = rec1.top + rec1.height / 2;
        var centerX2 = rec2.left + rec2.width / 2;
        var centerY2 = rec2.top + rec2.height / 2;
        var disX = Math.abs(centerX1 - centerX2); 
        var disY = Math.abs(centerY1 - centerY2);
        if (disX < (rec1.width + rec2.width) / 2 &&
            disY < (rec1.height + rec2.height) / 2
        ) {
            return true;
        }
        return false;
    }
    isGameOver() {
        if (this.bird.top === this.bird.maxY) {
            return true;
        }
        for (let i = 0; i < this.pipeProducer.pairs.length; i++) {
            const pair = this.pipeProducer.pairs[i];
            if (this.isHit(this.bird, pair.upPipe) || this.isHit(this.bird, pair.downPipe)) {
                return true;
            }
        }
        return false;
    }
    stop() {
        clearInterval(this.timer);
        this.timer = null;
        this.bird.stopSwing();
        this.pipeProducer.stopProduce();
    }
    regEvent() {
        window.onkeydown = (e) => {
            if (e.key === "Enter") {
                if (this.timer) {
                    this.stop();
                }
                else {
                    this.start();
                }
            }
            else if (e.key === " ") {
                this.bird.jump();
            }
        }
    }
}
var g = new Game();
g.regEvent();
</script>
</body>
</html>